// These are meant to be typed into the REPL. You can also run
// scala -Xnojline < 9_transfer.sc to run them all at once.
/**
 * 总结：
 * 协变就是自动往往上转型
 * 逆变就是自动向下转型
 *
 * 类型约束 & 判断
 * T =:= U 是否相等
 * T <:< U T是否为U的子类型
 * T <%< U T是否是可以被隐式转换为U
 * 1)协变是可以用自己替换需要自己父亲的位置是允许的，也就是当参数需要父类型参数
 *  时你可以传入子类型 （+T）
 *  eg： Student extends Person
 *       Pair[+T]
 *       def makeFriends2(p: Pair[Person])
 *       本来只允许传入参数 Pair[Person] 因为协变的原因传入参数 Pair[Student] 也是可以的
 * 2)逆变就是可以用父亲替换儿子的位置而允许的，也就是当参数需要子类型的时候
 * 可以传入父类型
 * eg： Student extends Person
 *      Pair[-T]
 *      def makeFriends2(p: Pair[Student])
 *      本来只允许传入参数 Pair[Student] 因为协变的原因传入参数 Pair[Person] 也是可以的
 * 3)不变就是不能改变参数类型
 */
// 这个不变的Pari不允许由Pair[Student] 转换为 Pair[Person]
// This invariant Pair class doesn't allow conversion of a 
// Pair[Student] to a Pair[Person]

class Person(name: String) {
  override def toString = getClass.getName + " " + name
}

class Student(val name: String) extends Person(name)

class Pair[T](val first: T, val second: T) {
  override def toString = "(" + first + "," + second + ")"
}

def makeFriends(p: Pair[Person]) =
  p.first + " and " + p.second + " are now friends."

def makeFriends2[ T <: Person](p: Pair[ T ]) =
  p.first + " and " + p.second + " are now friends."

val fred = new Student("Fred")
val wilma = new Student("Wilma")

val studentPair = new Pair(fred, wilma)

println("makeFriends ******** start ")
//makeFriends(studentPair) // Error
makeFriends2(studentPair) // OK
println("makeFriends ******** end ")
// 需要定义正向协变， [+T] 会自动隐式转换，可以接受 T 的超类
// Making Pair covariant allows conversion of a
// Pair[Student] to a Pair[Person]

class Pair[+T](val first: T, val second: T) {
  override def toString = "(" + first + "," + second + ")"
}

def makeFriends2(p: Pair[Person]) =
  p.first + " and " + p.second + " are now friends."

val studentPair = new Pair(fred, wilma)

println("makeFriends2 ******** start ")
makeFriends2(studentPair) // OK
println("makeFriends2 ******** end ")

// This contravariant Friend trait allows conversion of a 
// Friend[Person] to a Friend[Student]

trait Friend[-T] {
  def befriend(someone: T)
}

class Person(val name: String) extends Friend[Person] {
  override def toString = getClass.getName + " " + name
  def befriend(someone: Person) {
      println(this + " and " + someone + " are now friends.")
  }
}

class Student(name: String) extends Person(name)


def makeFriendWith3(s: Student, f: Friend[Student]) { f.befriend(s) }

val susan = new Student("Susan")
val fred = new Person("Fred")

println("makeFriendWith3 ******** start ")
makeFriendWith3(susan, fred) // Ok, Fred is a Friend of any person
println("makeFriendWith3 ******** end ")


// 一元函数转换为 Function1[-A, +R]
// A unary function has variance Function1[-A, +R]

def friends(students: Array[Student], find: Function1[Student, Person]) =
  for (s <- students) yield find(s)

//def friends(students: Array[Student], find: Student => Person]) =
//  for (s <- students) yield find(s)
//
// find: Function1[Student, Person] 的具体实现
def findFred(s: Student) = new Person(s.name)

friends(Array(susan, new Student("Barney")), findFred)
//res9: Array[Person] = Array(Person Susan, Person Barney)

